﻿using System;
using System.Text;
using Newtonsoft.Json;
using RabbitMQ.Client.MessagePatterns;

namespace smp.RabbitMq
{
    internal class Requester : RabbitMqBusBase, IRequester
    {
        internal Requester(IRabbitMqConfiguration configuration) 
            : base(configuration)
        {
        }

        ~Requester()
        {
            Dispose(false);
        }

        #region Implementation of IDisposable

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion

        #region implementation of IRequester

        public TResponse Request<TMessage, TResponse>(TMessage message)
        {
            return (TResponse)Request(typeof(TMessage).AssemblyQualifiedName, SerializeMessage(message), SingletonFactory.Instance.DefaultTimeOut);
        }

        public TResponse Request<TMessage, TResponse>(TMessage message, int timeOutMilliSeconds)
        {
            return (TResponse)Request(typeof(TMessage).AssemblyQualifiedName, SerializeMessage(message), timeOutMilliSeconds);
        }

        public object Request(Type messageType, object message)
        {
            return Request(messageType.AssemblyQualifiedName, SerializeMessage(message), SingletonFactory.Instance.DefaultTimeOut);
        }

        public object Request(Type messageType, object message, int timeOutMilliSeconds)
        {
            return Request(messageType.AssemblyQualifiedName, SerializeMessage(message), timeOutMilliSeconds);
        }

        public object Request(string assemblyQualifiedMessageType, string message)
        {
            return Request(assemblyQualifiedMessageType, message, SingletonFactory.Instance.DefaultTimeOut);
        }

        public object Request(string assemblyQualifiedMessageType, string message, int timeOutMilliSeconds)
        {
            var envelope = new Envelope(assemblyQualifiedMessageType, message);
            var serializedMessage = JsonConvert.SerializeObject(envelope);
            var messagePacket = Encoding.ASCII.GetBytes(serializedMessage);

            string serializedResponse;

            try
            {
                var client = new SimpleRpcClient(Channel, Configuration.Queue);
                client.TimeoutMilliseconds = timeOutMilliSeconds;
                var responsePacket = client.Call(messagePacket);
                serializedResponse = responsePacket == null ? null : Encoding.ASCII.GetString(responsePacket);
            }
            catch (Exception ex)
            {
                var error = new Exception(string.Format("An error was throws when trying to raise and process a request {0} on queue {1}.", serializedMessage, Configuration.Queue), ex);
                Log.Error(error);
                throw error;
            }

            if (string.IsNullOrEmpty(serializedResponse))
                return null;
            var responseEnvelope = DeserializeMessage<Envelope>(serializedResponse);
            if (responseEnvelope == null || responseEnvelope.MessageType == null || responseEnvelope.Message == null)
            {
                Log.Warning("Received null response");
                return null;
            }
            return DeserializeMessage(responseEnvelope.MessageType, responseEnvelope.Message);
        }

        #endregion
    }
}
